home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Monster Media 1996 #15
/
Monster Media Number 15 (Monster Media)(July 1996).ISO
/
os2
/
lxlt113.zip
/
SOURCES
/
LXLITE.PAS
< prev
next >
Wrap
Pascal/Delphi Source File
|
1996-06-05
|
33KB
|
983 lines
{$AlignCode-,AlignData-,AlignRec-,G3+,Speed-,Frame-}
uses use32, exe386, os2base, strOp, miscUtil, Helpers, Country,
Strings, Dos, Crt;
label done;
const Version = '1.1.3';
cfgFname = 'lxLite.cfg';
logFname = 'lxLite.log';
{-Configuration parameters-}
Verbose : boolean = _OFF;
objUnpack : boolean = _ON;
Backup : boolean = _OFF;
Pause : boolean = _OFF;
svFlags : Longint = svfFOalnNone + svfEOalnShift;
pkFlags : Longint = pkfLempelZiv;
doUnpack : boolean = _OFF;
ForceRp : boolean = _OFF;
ForceIdle : boolean = _ON;
RealignB : Byte = 2;
doWrite : boolean = _ON;
ShowCfg : boolean = _OFF;
rplStub : boolean = _OFF;
Recurse : boolean = _OFF;
QueryList : boolean = _OFF;
stubName : string = '';
excludeMask : string = '';
logFileName : string = '';
xdFileMask : string = '';
ddFileMask : string = '';
maxStubSz : Longint = 1024;
{-Confirmation query subsystem constants-}
askInUse = 1;
askExtraData= 2;
askOverBak = 3;
askConfirm = 4;
askDbgInfo = 5;
askFirst = askInUse;
askLast = askDbgInfo;
AskStatus : array[askFirst..askLast] of record
ID : char; {The /Y# character}
Reply : char; {What to answer}
end =
((ID : 'U'; Reply : #0),
(ID : 'X'; Reply : 'D'),
(ID : 'B'; Reply : 'N'),
(ID : 'C'; Reply : #0),
(ID : 'D'; Reply : 'Y'));
type pMyLX = ^tMyLX;
tMyLX = object(tLX)
procedure DisplayHeader;
end;
var fNames,
pfNames,
loadCFG : pDarray;
LX : pMyLX;
totalGain : Longint;
newStub : Pointer;
newStubSz : Longint;
allDone : boolean;
oldExit : Procedure;
exclude : pFileMatch;
logFile : Text;
Cntry : pCountry;
procedure tMyLX.DisplayHeader;
const
txtCPU : array[lxCPU286..lxCPUP5] of string[8] =
('i80286','i80386','i80486','Intel P5');
var S : String;
I : Longint;
procedure AddS(const nS : string);
begin
if S <> '' then S := S + ', ';
S := S + nS;
end;
begin
textAttr := $0B;
Writeln(#13'├');
textAttr := $0A;
S := '';
case Header.lxMFlags and lxModType of
lxEXE : begin
AddS('executable');
case Header.lxMFlags and lxAppMask of
lxNoPMwin : AddS('not PM windowed');
lxPMwin : AddS('PM windowed');
lxPMapi : AddS('PM application');
else AddS('unknown API type');
end;
end;
lxDLL,
lxPMDLL,
lxPDD,
lxVDD : begin
case Header.lxMFlags and lxModType of
lxDLL : AddS('DLL');
lxPMDLL : AddS('protmode DLL');
lxPDD : AddS('PDD');
lxVDD : AddS('VDD');
end;
if Header.lxMFlags and lxLibInit <> 0
then AddS('per-process Init');
if Header.lxMFlags and lxLibTerm <> 0
then AddS('per-process Term');
end;
else AddS('unknown module type');
end;
if Header.lxMFlags and lxNoIntFix <> 0
then AddS('no internal fixups');
if Header.lxMFlags and lxNoExtFix <> 0
then AddS('no external fixups');
if Header.lxMFlags and lxNoLoad <> 0
then AddS('not loadable');
Writeln('├ Module type: ', S);
Writeln('├ Required CPU: ', txtCPU[Header.lxCpu]:10, ' ',
'Version: ', long2str(Header.lxVer shr 16) + '.' + sstr(SmallWord(Header.lxVer), 2, '0'):10);
Writeln('├ Page size: ', Header.lxPageSize:10, ' ',
'Page shift: ', Header.lxPageShift:10);
Writeln('├ Objects: ', Header.lxObjCnt:10, ' ',
'Resources: ', Header.lxRsrcCnt:10);
Writeln('├ Imported entries:', Header.lxImpModCnt:7, ' ',
'Debug info,b: ', Header.lxDebugLen:10);
Writeln('├ Start ObjID:EIP: ', Header.lxStartObj,':',Hex8(Header.lxEIP));
Writeln('├ Stack ObjID:ESP: ', Header.lxStackObj,':',Hex8(Header.lxESP));
For i := 1 to ResNameTbl^.numItems do
with pNameTblRec(ResNameTbl^.GetItem(I))^ do
if Ord = 0
then Writeln('├ Module name: ', Name^);
For i := 1 to NResNameTbl^.numItems do
with pNameTblRec(NResNameTbl^.GetItem(I))^ do
if Ord = 0
then Writeln('├ Description: ', Name^);
Write('└ ');
end;
Procedure Stop(eCode : Byte);
Procedure Pause;
begin
if not RedirOutput
then begin
textAttr := $01; Write(Strg(' ', 30), 'Press any key ... '); Readkey;
Write(#13); textAttr := $07; ClrEOL;
end;
end;
begin
Write(#13);
case eCode of
1,2 : begin
if eCode = 2
then begin
TextAttr := 12;
Writeln('├ Invalid switch - see help below for details');
end;
TextAttr := 7;
Writeln('├ Usage: lxLite [FileMask1] {...FileMask2} {/ABCDEFIMPQRSTUVWXZH?}');
Writeln('├ /A{P|S|N{P|S}}');
Writeln('│ Set alignment for first/rest of objects. First object can be aligned');
Writeln('│ on [P]age shift, [S]ector or [N]o boundary. For rest you cannot use N');
Writeln('├ /B{+|-} Enable (+) or disable (-) renaming of original file into .BAK');
Writeln('├ /C{#} Use configuration with given (#) identifier (see /Q)');
Writeln('├ /D{#} Set exclu[D]e filemasks. Skip files that fit in given filemask');
Writeln('├ /E{+|-} r[E]cursive (+) file search through subdirectories');
Writeln('├ /F{+|-} Force (+) or don`t force (-) repacking. Use to bypass autodetection');
Writeln('├ /G[X|D]#Extra/debug data [G]oes into another file. (#) is an OS/2 filemask');
Writeln('├ /I{+|-} Run lxLite at [I]dle (+) or at normal (-) priority');
Writeln('├ /L{#} Set [L]og filename. If no filename is specified, lxLite.log is used');
Writeln('├ /M{R{N|1|2|3}|L{N|1}} Set packing method & parameters:');
Writeln('│ R = run-length (/EXEPACK:1); [N]one or level [1],[2],[3] (3=max comp. lvl)');
Writeln('│ L - kinda Lempel-Ziv (/EXEPACK:2); [N]one or level [1] (always the best)');
Writeln('├ /P{+|-} Enable (+) or disable (-) pause before each file');
Writeln('├ /Q{+|-} [Q]uery configuration options (/C#). Shows a list of cfg names.');
Writeln('├ /R{#} [R]e-align pages on specific boundary. (#) must be a power of two');
Writeln('├ /S{+|-} Show (+) or don`t show (-) current configuration (useful with /C#)');
Writeln('├ /T{#} Replace DOS stub by that contained in file #. Use /T to remove stub');
Pause;
Writeln('├ /U{+|-} Enable (+) or disable (-) unpacking file before packing');
Writeln('├ /V{+|-} Verbose (show a lot of additional file information)');
Writeln('├ /W{+|-} Enable (+) or disable (-) writing of resulting file');
Writeln('├ /X{+|-} e[X]pand given files');
Writeln('├ /Y{#{?} auto-repl[Y] "?" on question about # or Ask if ? is missing');
Writeln('├ /Z{#} Set stub size threshold: if stubSize > # then don`t replace it');
Writeln('├ /?,/H Show this help screen');
Writeln('├┤Default: /ANP /B- /Cdefault /D+ /E- /F- /G /I+ /MRN /ML1 /O256 /P- /Q-');
Writeln('│ /R4 /S- /T{disabled} /U+ /V- /W+ /X- /YBN /YDD /YXD /Z1024');
TextAttr := $08;
Writeln('└┤Example: lxLite *.exe *.dll *.fon *.sys *.pdr /e /d+ /p+ /ass');
end;
3 : Writeln('└┤Invalid entry in configuration file');
4 : Writeln('└┤Cannot load DOS stub replacement ', stubName);
5 : Writeln('└┤Fatal disk I/O error: cannot continue');
6 : Writeln('└┤Invalid stub format: not a DOS .EXE file');
7 : Writeln('└┤Failed to open configuration file');
8 : Writeln('└┤Failed to open log file ', logFileName);
9 : Writeln('└┤Cannot get country information');
10 : Writeln('└┤Option /G?#: Cannot conert filename using given filemask');
11 : Writeln('└┤Option /G?#: Xtra/debug filename equals executable filename');
12 : Writeln('└┤Cannot open file for xtra/debug data');
end;
Halt(eCode);
end;
Procedure LoadConfig(const ID : string); forward;
var Ch : Char;
Function ParmHandler(var S : string) : Byte;
var I : Longint;
Function Enabled : boolean;
begin
Enabled := _ON;
if length(S) = 1
then exit
else
if (S[2] in ['+','-'])
then ParmHandler := 2
else
if (S[2] in [' ','/'])
then exit
else Stop(2);
if S[2] = '-' then Enabled := _OFF;
end;
begin
ParmHandler := 1;
case upCase(S[1]) of
'?',
'H' : Stop(1);
'A' : if length(S) > 1
then begin
svFlags := svFlags and (not svfAlignFirstObj);
case upCase(S[2]) of
'N' : svFlags := svFlags or svfFOalnNone;
'P' : svFlags := svFlags or svfFOalnShift;
'S' : svFlags := svFlags or svfFOalnSector;
else Stop(2);
end;
ParmHandler := 2;
if length(S) > 2
then begin
svFlags := svFlags and (not svfAlignEachObj);
case upCase(S[3]) of
'P' : svFlags := svFlags or svfEOalnShift;
'S' : svFlags := svFlags or svfEOalnSector;
else Stop(2);
end;
ParmHandler := 3;
end;
end;
'C' : begin
Delete(S, 1, 1);
I := 0; While (I < length(S)) and (S[I + 1] > ' ') do Inc(I);
LoadConfig(Copy(S, 1, I));
ParmHandler := I;
end;
'R' : begin
Delete(S, 1, 1);
I := DecVal(S);
if I <> 0
then RealignB := BitSR(I)
else RealignB := 255;
ParmHandler := 0;
if not (RealignB in [0..12,255]) then Stop(2);
end;
'T' : begin
Delete(S, 1, 1);
I := 0; While (I < length(S)) and (S[I + 1] > ' ') do Inc(I);
stubName := Copy(S, 1, I); rplStub := _ON;
ParmHandler := I;
end;
'M' : if length(S) > 1
then case upCase(S[2]) of
'R' : begin
ParmHandler := 3;
pkFlags := pkFlags and not (pkfRunLength or pkfRunLengthLvl);
if length(S) > 2
then case upCase(S[3]) of
'1' : pkFlags := pkFlags or pkfRunLength or pkfRunLengthMin;
'2' : pkFlags := pkFlags or pkfRunLength or pkfRunLengthMid;
'3' : pkFlags := pkFlags or pkfRunLength or pkfRunLengthMax;
'N' : ;
else Stop(2);
end
else Stop(2);
end;
'L' : begin
ParmHandler := 3;
if length(S) > 2
then case upCase(S[3]) of
'1' : pkFlags := pkFlags or pkfLempelZiv;
'N' : pkFlags := pkFlags and not pkfLempelZiv;
else Stop(2);
end
else Stop(2);
end
else Stop(2);
end
else Stop(2);
'D' : begin
Delete(S, 1, 1);
I := 0; While (I < length(S)) and (S[I + 1] > ' ') do Inc(I);
if I = 0
then excludeMask := ''
else excludeMask := excludeMask + Copy(S, 1, I);
ParmHandler := I;
end;
'L' : begin
Delete(S, 1, 1);
I := 0; While (I < length(S)) and (S[I + 1] > ' ') do Inc(I);
if I >= 1
then logFileName := Copy(S, 1, I)
else logFileName := sourcePath + logFname;
ParmHandler := I;
end;
'G' : begin
Delete(S, 1, 1);
if (S = '') or (not (upCase(S[1]) in ['D','X'])) then Stop(2);
I := 1; While (I < length(S)) and (S[I + 1] > ' ') do Inc(I);
case upCase(S[1]) of
'D' : ddFileMask := Copy(S, 2, pred(I));
'X' : xdFileMask := Copy(S, 2, pred(I));
end;
ParmHandler := I;
end;
'B' : Backup := Enabled;
'F' : ForceRp := Enabled;
'I' : ForceIdle := Enabled;
'E' : Recurse := Enabled;
'Q' : QueryList := Enabled;
'S' : ShowCfg := Enabled;
'U' : objUnpack := Enabled;
'P' : Pause := Enabled;
'V' : Verbose := Enabled;
'W' : doWrite := Enabled;
'X' : begin
doUnpack := Enabled;
if doUnpack then LoadConfig('unpack');
end;
'Y' : if (length(S) > 1) and (S[2] > ' ')
then begin
ParmHandler := 2;
For I := askFirst to askLast do {Enable all queries}
with AskStatus[I] do
if UpCase(S[2]) = ID
then begin
if (length(S) > 2) and (S[3] > ' ')
then begin
Reply := S[3];
ParmHandler := 3;
end
else Reply := #0;
exit;
end;
Stop(2);
end
else For I := askFirst to askLast do {Enable all queries}
AskStatus[I].Reply := #0;
'Z' : begin
Delete(S, 1, 1);
if (S <> '') and (S[1] in ['0'..'9'])
then maxStubSz := DecVal(S)
else maxStubSz := $7FFFFFFF;
end;
else Stop(2);
end;
end;
Function NameHandler(var S : string) : Byte;
var I : Longint;
Quote : boolean;
begin
I := 0;
if S[1] = '"' then begin Quote := _ON; Delete(S, 1, 1); end else Quote := _OFF;
While (I < length(S)) and ((S[succ(I)] > ' ') or Quote) do
if Quote and (S[succ(I)] = '"')
then break
else Inc(I);
fNames^.AddItem(NewStr(Copy(S, 1, I)));
Inc(I, byte(Quote));
NameHandler := I;
end;
Procedure ShowConfigList;
var T : Text;
S,W : String;
I : Longint;
begin
Assign(T, sourcePath + cfgFname); Reset(T);
if ioResult <> 0 then Stop(7);
While not SeekEOF(T) do
begin
Readln(T, S);
if First(';', S) > 0 then Delete(S, First(';', S), 255);
DelStartSpaces(S);
if S = '' then continue;
I := First(':', S); if I = 0 then Stop(3);
W := Copy(S, 1, pred(I));
While (W[length(W)] = ' ') do Dec(byte(W[0]));
if length(W) < 10 then W := W + Strg(' ', 10 - length(W));
S := Copy(S, succ(I), 255); DelStartSpaces(S);
textAttr := $07; Write('├┤');
textAttr := $0A; Write(W);
textAttr := $02; Writeln(S);
end;
end;
Procedure LoadConfig;
var T : Text;
S : String;
I : Longint;
W : boolean;
begin
For I := 1 to loadCFG^.numItems do
if pString(loadCFG^.GetItem(I))^ = upStrg(ID) then exit; {already}
loadCFG^.AddItem(NewStr(upStrg(ID)));
Assign(T, sourcePath + cfgFname); Reset(T);
if ioResult <> 0 then Stop(7);
W := _OFF;
While not SeekEOF(T) do
begin
Readln(T, S);
if First(';', S) > 0 then Delete(S, First(';', S), 255);
DelStartSpaces(S);
if S = '' then continue;
if First(':', S) = 0 then Stop(3);
if upStrg(Copy(S, 1, pred(First(':', S)))) = upStrg(ID)
then begin
Delete(S, 1, First(':', S));
ParseCommandLine(S, ParmHandler, NameHandler);
W := _ON; break;
end;
end;
if not W
then begin
textAttr := $0C;
Writeln('├ Failed to load configuration record [', Copy(ID, 1, 20), ']');
end;
inOutRes := 0; Close(T); inOutRes := 0;
end;
Procedure ShowConfig;
const ONOFF : array[boolean] of string[3] = ('OFF', 'ON');
begin
textAttr := $0B;
Writeln('├ ═══════════ lxLite configuration: ═══════════');
textAttr := $03;
Writeln('├ Verbose: ', ONOFF[Verbose]);
Writeln('├ Run at idle priority: ', ONOFF[ForceIdle]);
Writeln('├ Unpack loaded executable: ', ONOFF[objUnpack]);
Writeln('├ Backup executables: ', ONOFF[Backup]);
Writeln('├ Pause before each file: ', ONOFF[Pause]);
if rplStub
then begin
Write('├ Replace DOS stub by: ');
if stubName <> ''
then Writeln(Copy(stubName, 1, 50))
else Writeln('remove it');
end;
Write ('├ Align first object: ');
case svFlags and svfAlignFirstObj of
svfFOalnNone : Writeln('No');
svfFOalnShift : Writeln('on PageShift bound');
svfFOalnSector : Writeln('on sector bound');
end;
Write ('├ Align other objects: ');
case svFlags and svfAlignEachObj of
svfEOalnShift : Writeln('on PageShift bound');
svfEOalnSector : Writeln('on sector bound');
end;
Write ('├ Realign executable pages: ');
if RealignB = 255
then Writeln('don`t change')
else Writeln('on ', 1 shl RealignB, ' boundary');
if not doUnpack
then begin
Write ('├ Run-length packing: ');
if pkFlags and pkfRunLength <> 0
then case pkFlags and pkfRunLengthLvl of
pkfRunLengthMin : Writeln('Minimal (find 1-byte sequences)');
pkfRunLengthMid : Writeln('Middle (up to 16-byte sequences)');
pkfRunLengthMax : Writeln('Maximal (find ALL sequences (SLOW!!!))');
end
else Writeln('Disabled');
Write ('├ Lempel-Ziv packing: ');
if pkFlags and pkfLempelZiv <> 0
then Writeln('Enabled')
else Writeln('Disabled');
end;
if excludeMask <> ''
then Writeln('├ Excluded files mask: ', excludeMask);
end;
Procedure MyExitProc;
begin
if TextRec(logFile).Handle <> 0 then Close(logFile);
Write(#13);
TextAttr := $07; ClrEOL;
OldExit;
end;
Function CheckError(ec : byte) : boolean;
begin
textAttr := $0C;
case ec of
lxeReadError : Write('error reading executable');
lxeWriteError : Write('error writing executable');
lxeBadFormat : Write('invalid executable file format');
lxeBadRevision : Write('unsupported executable format revision');
lxeBadOrdering : Write('invalid word/dword ordering in executable');
lxeInvalidCPU : Write('executable target is an unsupported CPU type');
lxeBadOS : Write('executable target is an unsupported OS');
lxeUnkEntBundle : Write('unknown entry bundle type in executable');
lxeUnkPageFlags : Write('unknown page flags in executable');
lxeInvalidPage : Write('invalid object page detected in executable');
lxeNoMemory : Write('not enough memory to load executable');
lxeInvalidStub : Write('invalid stub');
lxeEAreadError : Write('error reading EAs');
lxeEAwriteError : Write('error writing EAs');
end;
if ec <> lxeOK
then begin
textAttr := $0B; Writeln(#13'├');
CheckError := _ON;
end
else CheckError := _OFF;
end;
var prevProgressValue : Longint;
function showProgress(Current,Max : Longint) : boolean;
var S : string;
val : Longint;
begin
S := Strg('▒', 20);
val := Current * 20 div Max;
if val <> prevProgressValue
then begin
FillChar(S[1], val, '█');
textAttr := $03;
Write(S,']' + Strg(#8, length(S) + 2) + '[');
prevProgressValue := val;
end;
end;
Function Ask(const Q,A : string; qNo : byte) : byte;
var ch : char;
N : Integer;
begin
ch := AskStatus[qNo].Reply;
N := First(upCase(ch), A);
if N <> 0 then begin Ask := N; exit; end;
TextAttr := $02;
Write('└ ', Q, ' ');
repeat
ch := upCase(ReadKey);
if First(ch, A) <> 0
then begin
Ask := First(ch, A);
break;
end;
until _OFF;
Writeln(Ch, #13'├');
end;
var askU : byte;
Function CheckUseCount(fName : string) : boolean;
var F : File;
I : Longint;
begin
CheckUseCount := _OFF; askU := 0;
I := FileMode; FileMode := open_access_ReadWrite or open_share_DenyReadWrite;
Assign(F, fName); SetFattr(F, Archive);
Reset(F, 1); Close(F); FileMode := I;
if ioResult = 0 then exit;
textAttr := $0E;
Writeln('├ The module ' + Copy(fName, 1, 40) + ' is used by another process');
CheckUseCount := _ON;
askU := Ask('[R]eplace, [S]kip or [A]bort?', 'RSA', askInUse);
case askU of
1 : ;
2 : exit;
3 : begin allDone := _ON; exit; end;
end;
fName := fName + #0;
if DosReplaceModule(@fName[1], nil, nil) <> 0
then begin
textAttr := $0C;
Writeln('├ Cannot replace module ' + fName);
exit;
end;
CheckUseCount := _OFF;
end;
Procedure StoreData(const fName,fMask : string; var destF : string;
var Buff; BuffSize : Longint);
var Source,
Mask,
Target : array[0..255] of Char;
F : File;
_d : DirStr;
_n : NameStr;
_e : ExtStr;
begin
if fMask = '' then Exit;
fSplit(fName, _d, _n, _e);
StrPcopy(Source, _n + _e);
StrPcopy(Mask, fMask);
if DosEditName(1, Source, Mask, Target, sizeOf(Target)) <> 0 then Stop(10);
if StrComp(Source, Target) = 0 then Stop(11);
destF := _d + StrPas(Target);
Assign(F, destF); Rewrite(F, 1);
if ioResult <> 0 then Stop(12);
BlockWrite(F, Buff, BuffSize);
inOutRes := 0; Close(F); inOutRes := 0;
end;
Procedure ProcessFile(fName : string);
label SaveLX;
var _d : DirStr;
_n : NameStr;
_e : ExtStr;
bk,
dbgOut,
xtrOut: string;
ss,fs : Longint;
askD,
askX,
askB : Byte;
Procedure TrackProcess;
begin
textAttr := $0B; Write(#13); ClrEOL;
Write('└ Processing file ', Copy(_n + _e, 1, 28) + ' ');
end;
begin
fSplit(fName, _d, _n, _e);
if exclude^.Matches(_n + _e) then Exit;
TrackProcess;
askD := 0; askX := 0; askB := 0; askU := 0; dbgOut := ''; xtrOut := '';
if CheckError(LX^.Load(fName)) then exit;
if LX^.Header.lxDebugLen > 0
then begin
Write(#13); ClrEOL;
textAttr := $0E;
Writeln('├ The file ' + Copy(_n + _e, 1, 28) + ' contains ' + long2str(LX^.Header.lxDebugLen) +
' bytes of debug information');
askD := Ask('[D]iscard or [L]eave them, [S]kip file or [A]bort ?', 'DLSA', askDbgInfo);
case askD of
1 : with LX^ do
if Header.lxDebugInfoOfs <> 0
then begin
StoreData(fName, ddFileMask, dbgOut, DebugInfo, Header.lxDebugLen);
FreeMem(DebugInfo, Header.lxDebugLen);
Header.lxDebugInfoOfs := 0;
Header.lxDebugLen := 0;
end;
3 : exit;
4 : begin allDone := _ON; exit; end;
end;
TrackProcess;
end;
if (not ForceRp) and (LX^.isPacked(realignB, newStubSz, pkFlags, svFlags))
then begin
Write('already processed'); textAttr := $0B; Writeln(#13'├');
exit;
end;
with LX^ do
if OverlaySize <> 0
then begin
Write(#13); ClrEOL;
textAttr := $0E;
Writeln('├ The file ' + Copy(_n + _e, 1, 28) + ' contains ' + long2str(OverlaySize) +
' bytes of data out of LX structure');
Write('├ It is possible that resulting file will be non-functional');
askX := Ask('[D]iscard or [L]eave them, [S]kip file or [A]bort ?', 'DLSA', askExtraData);
case askX of
1 : begin
StoreData(fName, xdFileMask, xtrOut, Overlay, OverlaySize);
FreeMem(Overlay, OverlaySize);
OverlaySize := 0;
end;
3 : exit;
4 : begin allDone := _ON; exit; end;
end;
TrackProcess;
end;
if rplStub and (LX^.StubSize <= maxStubSz) and (newStubSz <> -1)
then with LX^ do
begin
FreeMem(Stub, StubSize);
GetMem(Stub, NewStubSz);
Move(NewStub^, Stub^, NewStubSz);
StubSize := NewStubSz;
end;
ss := FileLength(fName);
if Verbose then LX^.DisplayHeader;
if RealignB <> 255 then LX^.Header.lxPageShift := RealignB;
if objUnpack then LX^.Unpack;
if not doUnpack
then begin
prevProgressValue := -1;
LX^.Pack(pkFlags, showProgress);
end;
Write(#13); ClrEOL;
if not doWrite then exit;
if CheckUseCount(fName) then exit;
bk := _d + _n + '.bak';
if FileExist(bk)
then begin
textAttr := $0E;
Writeln('├ The file ' + bk + ' already exists.');
askB := Ask('[O]verwrite .BAK/[N]o backup/[S]kip file or [A]bort?', 'ONSA', askOverBak);
case askB of
1 : FileErase(bk);
2 : goto SaveLX;
3 : exit;
4 : begin allDone := _ON; exit; end;
end;
end;
textAttr := $0B; Write('└ Backing up ', Copy(_n + _e, 1, 28) + ' ... ');
if not FileCopy(fName, bk)
then begin
textAttr := $0C; Write('error during copy');
textAttr := $0B; Writeln(#13'├');
exit;
end;
Write(#13); ClrEOL;
SaveLX:
textAttr := $0B; Write('└ Saving file ', Copy(_n + _e, 1, 28) + ' ... ');
if CheckError(LX^.Save(fName, svFlags))
then begin
if not FileCopy(bk, fName) then Stop(5);
FileErase(bk);
exit;
end;
if not Backup then FileErase(bk);
Write(#13); ClrEOL;
fs := FileLength(fName);
textAttr := $0B;
_d := long2str(1000 - (fs * 1000) div ss);
If (length(_d) < 2 + byte(_d[1] = '-'))
then Insert('0.', _d, length(_d))
else Insert('.', _d, length(_d));
Writeln('├', Copy(_n + _e, 1, 28):28, ' initial:',
SStr(ss, 8, ' '), ' final:', SStr(fs, 8, ' '),
' gain: ', _d, '%');
Inc(totalGain, ss - fs);
if logFileName <> ''
then begin
Writeln(logFile, Cntry^.TimeStr(toStdTimeL),
' File: ', fName, Strg(' ', 20 - length(fName)),
' initial:', SStr(ss, 8, ' '), ' final:', SStr(fs, 8, ' '),
' gain: ', _d, '%');
case askD of
1 : if dbgOut <> ''
then Writeln(logFile, Strg(' ', 9), 'Debug info has been placed into ', dbgOut)
else Writeln(logFile, Strg(' ', 9), 'Debug info has been removed from output file');
2 : Writeln(logFile, Strg(' ', 9), 'Debug info has been re-stored into output file');
end;
case askX of
1 : if xtrOut <> ''
then Writeln(logFile, Strg(' ', 9), 'Extra LX data has been placed into ', xtrOut)
else Writeln(logFile, Strg(' ', 9), 'Extra LX data has been removed from output file');
2 : Writeln(logFile, Strg(' ', 9), 'Extra LX data has been re-stored into output file');
end;
case askB of
1 : Writeln(logFile, Strg(' ', 9), '.BAK file already existed and has been overwritten');
2 : Writeln(logFile, Strg(' ', 9), '.BAK file already existed and left as-is');
end;
case AskU of
1 : Writeln(logFile, Strg(' ', 9), 'Executable has been used by another process and replaced');
end;
end;
end;
Procedure freeFnames;
var i : SmallInt;
begin
For i := 1 to fNames^.numItems do
DisposeStr(fNames^.GetItem(I));
fNames^.Clear;
end;
Procedure clearProcessed;
var i : longint;
begin
For I := 1 to pfNames^.NumItems do
DisposeStr(pfNames^.GetItem(I));
pfNames^.Clear;
end;
Function CheckIfProcessed(const fName : string) : boolean;
var i : longint;
s : String;
begin
CheckIfProcessed := _ON;
s := lowStrg(fExpand(fName));
For I := 1 to pfNames^.numItems do
if pString(pfNames^.GetItem(I))^ = s
then exit;
pfNames^.AddItem(NewStr(s));
CheckIfProcessed := _OFF;
end;
Procedure LoadStub;
type
pDosEXEheader = ^tDosEXEheader;
tDosEXEheader = record
ID : SmallWord;
PartPage : SmallWord;
PageCount : SmallWord;
ReloCount : SmallWord;
HeaderSize: SmallWord;
MinAlloc : SmallWord;
MaxAlloc : SmallWord;
InitSS : SmallWord;
InitSP : SmallWord;
CheckSum : SmallWord;
InitIP : SmallWord;
InitCS : SmallWord;
RelTblOfs : SmallWord;
Overlay : SmallWord;
dummy : array[1..16] of SmallWord;
ExtHdrOfs : Longint;
end;
var F : File;
EH : pDosEXEheader;
P : pArrOfByte;
S,hS : Longint;
begin
if (not rplStub) then begin NewStubSz := -1; exit; end;
if (stubName = '') then exit;
Assign(F, stubName); Reset(F, 1);
if ioResult <> 0
then begin Assign(F, SourcePath + stubName); Reset(F, 1); end;
if ioResult <> 0 then Stop(4);
newStubSz := FileSize(F);
GetMem(newStub, newStubSz);
BlockRead(F, newStub^, newStubSz);
Close(F);
if ioResult <> 0 then Stop(4);
EH := newStub;
with EH^ do
begin
if (ID <> $4D5A) and (ID <> $5A4D) then Stop(6);
if RelTblOfs < $40
then begin
hS := ($40 + ReloCount * 4 + 15) and $FFFFFFF0;
S := hS + (PageCount * 512 - (512 - PartPage) - HeaderSize * 16);
GetMem(P, S); FillChar(P^, S, 0);
Move(newStub^, P^, RelTblOfs);
pDosEXEheader(P)^.RelTblOfs := $40;
pDosEXEheader(P)^.HeaderSize := hS shr 4;
pDosEXEheader(P)^.PageCount := (S + 511) shr 9;
pDosEXEheader(P)^.PartPage := S and 511;
Move(pArrOfByte(newStub)^[RelTblOfs], P^[$40], ReloCount * 4);
Move(pArrOfByte(newStub)^[HeaderSize * 16], P^[hS], S - hS);
FreeMem(newStub, newStubSz);
newStub := P; newStubSz := S;
end;
end;
end;
Procedure ProcessFiles(const fN : string; Level : Longint);
var sr : SearchRec;
_d : DirStr;
_n : NameStr;
_e : ExtStr;
nf : Longint;
begin
ClearProcessed;
fSplit(fN, _d, _n, _e);
FindFirst(fN, Archive or Hidden or SysFile, sr);
if (DosError <> 0) and (Level = 0) and (not Recurse)
then begin
textAttr := $0C;
Writeln('├ Cannot find such files: ', fN);
end;
nf := 0;
While (DosError = 0) and (not allDone) do
begin
if not CheckIfProcessed(_d + sr.Name)
then begin
Inc(nf);
if Pause
then case Ask('File ' + sr.Name + ': [P]rocess, [S]kip or [A]bort?', 'PSA', askConfirm) of
2 : sr.Name := '';
3 : begin allDone := _ON; break; end;
end;
if (sr.Name <> '') then ProcessFile(_d + sr.Name);
end;
FindNext(sr);
end;
FindClose(sr);
if allDone or not Recurse then Exit;
if nf = 0 then begin textAttr := $0B; Write('└ ', _d); ClrEOL; Write(#13); end;
FindFirst(_d + '*.*', Archive or Hidden or SysFile or Directory, sr);
While (dosError = 0) and (not allDone) do
begin
if (sr.Attr and Directory <> 0) and (sr.Name[1] <> '.')
then ProcessFiles(_d + sr.Name + '\' + _n + _e, succ(Level));
FindNext(sr);
end;
FindClose(sr);
end;
var I : longint;
begin
TextAttr := $0F;
Writeln('┌[ lxLite ]──────────────────────────────[ Version '+Version+' ]┐');
Writeln('├ Copyright 1996 by FRIENDS software ─ No rights reserved ┘');
TextAttr := $07;
@OldExit := ExitProc; ExitProc := @MyExitProc;
HeapBlock := 64 * 1024;
New(loadCFG, Init(8));
New(LX, Init);
New(Cntry, Init(cyDefault, cpDefault));
if Cntry = nil then Stop(9);
New(fNames, Init(8));
LoadConfig('default');
ParseCommandLine(#0, ParmHandler, NameHandler);
if QueryList then begin ShowConfigList; Goto Done; end;
if (fNames^.numItems = 0) and (not ShowCfg) then Stop(1);
LoadStub;
New(pfNames, Init(8));
if ForceIdle then DosSetPriority(Prtys_ProcessTree, Prtyc_IdleTime, 16, 0);
if logFileName <> ''
then begin
Assign(logFile, logFileName);
Append(logFile); if ioResult <> 0 then Rewrite(logFile);
if ioResult <> 0 then Stop(8);
Writeln(logFile, '-------- ', Cntry^.DateStr(doStdDateL), ' at ',
Cntry^.TimeStr(toStdTimeL), ' started lxLite v', Version);
end;
if doUnpack
then begin
objUnpack := _ON;
PkFlags := PkFlags and not (pkfRunLength or pkfLempelZiv);
end;
if ShowCfg then ShowConfig;
New(exclude, Init(excludeMask));
For I := 1 to fNames^.numItems do
begin
ProcessFiles(pString(fNames^.GetItem(I))^, 0);
if allDone then break;
end;
ClrEOL;
freeFnames; Dispose(fNames, Done);
clearProcessed; Dispose(pfNames, Done);
Dispose(exclude, Done);
For I := 1 to loadCFG^.numItems do
DisposeStr(loadCFG^.GetItem(i));
Dispose(loadCFG, Done);
Dispose(LX, Done);
if newStubSz <> -1 then FreeMem(newStub, newStubSz);
if totalGain <> 0
then begin
TextAttr := $03;
Writeln('├┤Total gain: ', totalGain, ' bytes');
end;
if logFileName <> ''
then Writeln(logFile, '-------- Total gain: ', totalGain, ' bytes');
done:
TextAttr := $01;
Writeln('└┤Done');
end.